Ligação com Lua

Introdução

A biblioteca CDLua foi desenvolvida para disponibilizar todas as funcionalidades da biblioteca CD para programadores em LUA. Foi escrita em C, com auxílio do utilitário toLua (uma ferramenta para gerar automaticamente ligações C/C++ para LUA).

Para utilizar as ligações com a biblioteca CDLua, seu executável deve estar associado ela e você deve chamar a função de inicialização cdlua_open(), definida no arquivo de inclusão cdlua.h, conforme exibido no exemplo a seguir:

#include <iup.h>
#include <lua.h>
#include <lualib.h>
#include <iuplua.h>
#include <cdlua.h>
void main(void)
{
  IupOpen();
  iolib_open();
  strlib_open();
  mathlib_open();
  cdlua_open();
  iuplua_open();
  lua_dofile("myprog.lua");
  IupMainLoop();
  IupClose();
}

A função cdlua_open( ) registra todas as funções e constantes da biblioteca CD de que seu programa em LUA necessita. O uso das funções de CDLua em LUA é geralmente idêntico aos seus equivalentes em C. Contudo, existem diversas exceções, devido às diferenças entre as duas linguagens.

Assim como na biblioteca CD, é importante ter certeza de que há um canvas atualmente ativo antes de chamar qualquer função de CDLua. Caso contrário, o programa LUA será abortado com uma mensagem de erro. Os canvas são criados e ativados da mesma forma que em C:

cdlua_canvas = cdCreateCanvas(CD_IUP, iuplua_canvas)
if cdlua_canvas == nil then
  -- deal with error
  ...
else
  cdActivate(cdlua_canvas)
end

Note que, ao contrário de C, em que as variáveis de estado são combinadas com o operador bit a bit OR, em LUA as variáveis de estado são adicionadas de forma aritmética.

Para verificar qual a versão da biblioteca de ligação com Lua utilize a variável global CDLUA_VERSION, que contém uma descrição como a variável _VERSION de Lua, por exemplo "CDLua 1.3.0".

Tipos de Dados

Os tipos de dados nativos de LUA não são suficientes para a ligação de CDLua, pois a biblioteca CD lida com uma série de tipos de dados diferentes de strings e números. Assim, visando alcançar a eficiência necessária, foram usados usertags para implementar esses tipos de dados. São eles:

color_tag: tipo usado pelas funções cdEncodeColor, cdDecodeColor, cdPixel, cdForeground e cdBackground.
imageex_tag: tipo usado pelas funções cdCreateImageEx, cdKillImageEx, cdGetImageEx e cdPutImageEx.
image_tag: tipo usado pelas funções cdCreateImage, cdKillImage, cdGetImage e cdPutImage.
imagergb_tag: tipo usado pelas funções cdCreateImageRGB, cdKillImageRGB, cdGetImageRGB e cdPutImageRGB.
imagergba_tag: tipo usado pelas funções cdCreateImageRGBA, cdKillImageRGBA e cdPutImageRGBA.
imagemap_tag: tipo usado pelas funções cdCreateImageMap, cdKillImageMap e cdPutImageMap.
palette_tag: tipo usado pelas funções cdCreatePalette, cdKillPalette, cdPalette e cdPutImageMap.
pattern_tag: tipo usado pelas funções cdCreatePattern, cdKillPattern e cdPattern.
stipple_tag: tipo usado pelas funções cdCreateStipple, cdKillStipple e cdStipple.

Novas Funções

Novas funções (sem equivalentes em C) foram implementadas para criar e destruir objetos de novos tipos de dados. Foram criadas funções de criação e remoção de imagens, pattern, stipple e palette.

Em caso de erro (por exemplo, falta de memória), as funções de criação dão como retorno nil. O usuário deve verificar seu valor de retorno. Veja o exemplo abaixo:

pattern = cdCreatePattern(16, 16)
if pattern == nil then
  ...
end

Todos os tipos novos podem ter seus valores verificados ou modificados diretamente, como se fossem tabelas em LUA:

pattern[y*16 + x] = cdEncodeColor(r, g, b)
...
color = pattern[y*16 + x]
r, g, b = cdDecodeColor(color)
...
cdPattern(pattern)

Note que o tipo de valor dado como retorno ou recebido por pattern[i] e palette[i] é color_tag, o mesmo tipo utilizado com as funções cdEncodeColor, cdDecodeColor, cdPixel, cdForeground e cdBackground.

Os tipos imageex, imagergb e imagergba são mais complexos, funcionando como tabelas de canais. Cada canal funciona como uma tabela de valores que deve ser consultada ou modificada da seguinte maneira:

imagergb = cdCreateImageRGB(100, 200)
...
imagergb.r[y*100 + x] = red
...
green = imagergb.g[y*100 + x]

Note ainda que é sempre importante definir o índice nos canais, pois, por exemplo, o tipo de imagergb.r (sem índice), channel_tag, é interno à implementação de CDLua e inútil para o usuário final. A ordem das tabelas é importante, de forma que imagergb[n].r não tem significado para a biblioteca CDLua e a expressão provocará um erro fatal. Finalmente, o usuário final pode esperar que o valor de imagergb[n] seja do tipo color_tag. Infelizmente, este não é o caso, e essa expressão provocará o mesmo erro fatal.

Os nomes conhecidos de canais são: r, g, b, a, i, c (os dois útlimos usado apenas com imageex).

Funções Modificadas

Algumas funções foram modificadas para receber parâmetros dos tipos imagergb_tag, imagergba_tag, imagemap_tag, palette_tag, stipple_tag e pattern_tag. Esses objetos já têm suas dimensões armazenadas internamente, de forma que o usuário não precisa passá-las como parâmetros.

As funções que recebem valores por referência em C também foram modificadas em LUA. Em geral, os valores dos parâmetros que teriam seus valores modificados são agora dados como retorno pela função na mesma ordem.

As funções ainda possuem a mesma funcionalidade, porém são utilizadas de forma diferente:

w, h = cdGetCanvasSize()
red, green, blue = cdDecodeColor(CD_DARK_MAGENTA)
y = cdUpdateYAxis(y)

Verificação de Erros

Uma rigorosa verificação de parâmetros é realizada por funções da biblioteca CDLua antes de passá-los à CD. Quando um erro é considerado fatal, a biblioteca interrompe o programa LUA e exibe uma mensagem de erro explicativa. Erros no número de parâmetros, erros nos tipos de parâmetros e inconsistência nos valores dos parâmetros são todos considerados erros fatais. Geralmente, os erros fatais são aqueles que exigem uma modificação no código em LUA e que interromperiam um programa equivalente em C.

Todos os erros fatais resultam em uma chamada a lua_error com o formato "function: message", onde function é o nome da função que detectou o erro fatal e message é a mensagem que identifica o erro. Alguns dos erros mais importantes estão listados nos exemplos abaixo:

"cdlua: there is no active canvas!"
"cdActivate: attempt to activate a killed canvas!"
"cdCreateCanvas: unknown driver!"
"cdKillStipple: attempt to kill a killed stipple!" 
"cdCreateCanvas CD_IUP: data should be an iuplua canvas object!"
"cdCreateCanvas CD_IMAGE: data is a NIL image!"
"cdCreateCanvas CD_PRINTER: data should be of type string!"
"cdPlay: CD_SIZECB: invalid return value!"
"cdCreateCanvas CD_SIMULATE: too many parameters!"
"cdRegisterCallback: unknown callback!"
"cdPlay: driver does not implement the play function or unknown driver!"
"cdKillCanvas: attempt to kill a NIL canvas!"
"cdEncodeColor: color components values should be in range [0, 255]!"
"cdCreateStipple: stipple dimensions should be positive integers!"

As fallbacks utilizadas para modificar e verificar os valores dos tipos imagergb_tag, imagergba_tag, imagemap_tag, palette_tag, pattern_tag e stipple_tag também realizam a verificação de erros e podem detectar erros fatais. Os métodos verificam os limites dos vetores e tipos e valores de parâmetros. As mensagens têm a forma "type 'fallback': message", conforme ilustrado a seguir:

"pattern_tag "settable": index should be a number!"
"imagemap_tag "gettable": index out of bounds!"
"stipple_tag "settable": value should belong to {0, 1}!"
"pattern_tag "settable": value should be of type color_tag!"

Integração com IMLua

A biblioteca CDLua é totalmente integrada com a IMLua, apesar de as duas bibliotecas serem completamente independentes. Sempre que elas forem inicializadas (a ordem não importa), cada uma é capaz de detectar a presença da outra e permitir a interação. Por serem independentes, cada biblioteca oferece seu próprio conjunto de funções para criar, acessar, modificar e destruir os novos tipos de dados. De fato, elas partilham os mesmos valores de tag para os tipos de dados comuns às bibliotecas CD e IM, de forma que os objetos criados pela CDLua são indistinguíveis daqueles criados pela IMLua. O exemplo abaixo mostra um código CDLua/IMLua perfeitamente válido.

...
w, h = imImageInfo(filename)
image = cdCreateImageRGB(w, h)
imLoadRGB(filename, image)
cdPutImageRGB(image, x, y, w, h) 
imKillImageRGB(image)